package main

import (
	"fmt"
	"net"
	"encoding/binary"
)

type PassError interface {
	Message() string
	ErrCode() uint16
}
type dbErr struct {
	code uint16
	msg  string
}

func (de *dbErr) ErrCode() uint16 {
	return de.code
}
func (de *dbErr) Message() string {
	return de.msg
}

func (de *dbErr) String() string {
	return fmt.Sprintf("Error(code=%d ,msg=%s )", de.code, de.msg)
}

type RawPacket struct {
	Len uint32
	Seq uint8
	Raw []byte
}

func (rp *RawPacket) Data() []byte {
	return rp.Raw[4:]
}
func (rp *RawPacket) String() string {
	return fmt.Sprintf("RawPacket(Len=%d ,Seq= %d,Raw= ... )", rp.Len, rp.Seq)
}

type Request struct {
	Remote net.Addr
	Raw    *RawPacket
}

func (r *Request) Kind() Kind {
	return Kind(r.Raw.Raw[4])
}

func (r *Request) QueryString() string {
	if r.Kind() != comQuery {
		return ""
	}
	return string(r.Raw.Data()[1:])
}
func readInteger(bits []byte) uint64 {
	lb := len(bits)
	if lb < 1 {
		return 0
	}
	var nl uint
	var value uint64
	switch bits[0] {
	case 0xfc:
		nl = 2
	case 0xfd:
		nl = 3
	case 0xfe:
		nl = 8
	default:
		nl = 0
		value = uint64(bits[0])
	}
	if uint(lb) < 1+nl {
		return 0
	}
	var i uint
	for i = 0; i < nl; i++ {
		bv := bits[i+1]
		value += uint64(bv) << (i * 8)
	}
	return value
}
func readString(bits []byte) string {
	var i int
	for {
		v := bits[i]
		if v == 0 {
			break
		}
		i++
	}
	return string(bits[:i])
}

type RespHeader struct {
	Raw *RawPacket
	err PassError
}

func (rh *RespHeader) FieldsNum() uint64 {
	return readInteger(rh.Raw.Data())
}
func (rh *RespHeader) OK() bool {
	return rh.Raw.Data()[0] == 0x00
}
func (rh *RespHeader) Err() PassError {
	if rh.err != nil {
		return rh.err
	}
	if rh.Raw.Data()[0] != 0xff {
		return nil
	}
	ec := binary.LittleEndian.Uint16(rh.Raw.Data()[1:])
	msg := string(rh.Raw.Data()[9:])
	rh.err = &dbErr{code: ec, msg: msg}
	return rh.err
}

type Response struct {
	Header *RespHeader
	Fields *PacketList
	Rows   *PacketList
}

func (resp *Response) Length() int {
	v := 1
	if resp.Fields != nil {
		v += resp.Fields.Length()
	}
	if resp.Rows != nil {
		v += resp.Rows.Length()
	}
	return v
}
func (resp *Response) Size() uint64 {
	v := uint64(len(resp.Header.Raw.Raw))
	var i PacketIterator
	if resp.Fields == nil {
		goto ret
	}
	i = resp.Fields.Iterator()
	for i.HasNext() {
		p := i.Next()
		v += uint64(len(p.Raw))
	}
	v += uint64(len(resp.Fields.EOF.Raw))
	i = resp.Rows.Iterator()
	for i.HasNext() {
		p := i.Next()
		v += uint64(len(p.Raw))
	}
	v += uint64(len(resp.Rows.EOF.Raw))
ret:
	return v
}
